﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace Vit.Web.Mvc
{
    public class MetadataDescriptor
    {
        private Type modelType;
        private Type metadataType;
        private AttributeCollection modelAttributes;
        private Dictionary<string, AttributeCollection> propertyAttributes;
        private ICustomTypeDescriptor metadataTypeDescriptor;
        private object metadataInstance;
        private object instance;
        private AssociatedMetadataTypeTypeDescriptionProvider provider;

        public MetadataDescriptor(Type type)
        {
            modelType = type;
            provider = new AssociatedMetadataTypeTypeDescriptionProvider(type);
            var metaTypeAttribute = provider.GetTypeDescriptor(type).GetAttributes().OfType<MetadataTypeAttribute>().FirstOrDefault();
            if (metaTypeAttribute == null)
                throw new Exception(string.Format("{0} has no MetadataTypeAttribute definition.", type.FullName));

            metadataType = metaTypeAttribute.MetadataClassType;

            metadataTypeDescriptor = provider.GetTypeDescriptor(metadataType);
            modelAttributes = metadataTypeDescriptor.GetAttributes();

            propertyAttributes = new Dictionary<string, AttributeCollection>(StringComparer.OrdinalIgnoreCase);

            foreach (PropertyDescriptor propDesp in metadataTypeDescriptor.GetProperties())
            {
                propertyAttributes.Add(propDesp.Name, propDesp.Attributes);
            }
        }

        public ModelMetadata GetModelMetadata(object obj)
        {
            return ModelMetadataProviders.Current.GetMetadataForType(() => obj, obj.GetType());
        }

        public ModelMetadata GetPropertyMetadata(object obj, string propertyName)
        {
            ModelMetadata md = GetModelMetadata(obj);
            return md.Properties.FirstOrDefault(o => o.PropertyName == propertyName);
        }

        public IEnumerable<Attribute> GetModelAttributes()
        {
            return modelAttributes.Cast<Attribute>();
        }

        public IEnumerable<Attribute> GetPropertyAttributes(string propertyName)
        {
            if (propertyAttributes.ContainsKey(propertyName))
                return propertyAttributes[propertyName].Cast<Attribute>();
            return null;
        }

        public object MetadataInstance
        {
            get
            {
                if (metadataInstance == null)
                {
                    metadataInstance = Activator.CreateInstance(metadataType);
                }

                return metadataInstance;
            }
        }

        public object Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = Activator.CreateInstance(modelType);
                }

                return instance;
            }
        }

        public Type ModelType
        {
            get
            {
                return modelType;
            }
        }

        public Type MetadataType
        {
            get
            {
                return metadataType;
            }
        }

        public ICustomTypeDescriptor MetadataTypeDescriptor
        {
            get
            {
                return metadataTypeDescriptor;
            }
        }
    }
}
